package com.wejoy.video.middle.station.data.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wejoy.video.middle.station.data.constant.RedisKeys;
import com.wejoy.video.middle.station.data.domain.system.SysUser;
import com.wejoy.video.middle.station.data.util.baiduevs.EvsApiHttpUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 登录用户的工具类
 */
@Component
public class LoginUtil {
    private final static Logger logger = LoggerFactory.getLogger(LoginUtil.class);
    private static RedisTemplate redisTemplate;
    private static ValueOperations<String, String> valueOps;
    /**
     * 登录用户在session中的key
     */
    public static final String LOGIN_USER_SESSION_KEY = "appUserSessionKey";
    /**
     * 50个线程, 无界队列, 超过异常
     */
    private final static ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(50, 50, 120, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());

    public static SysUser getLoginUser(String token) {
        WJAssert.isMaxLength(token, 500, "token非法");
        String key = RedisKeys.getSystemUserSessionKey(token);
        com.alibaba.fastjson2.JSONObject currentUserInfo = EvsApiHttpUtils.getCurrentUserInfo(token);
        String redirect = currentUserInfo.getString("redirect");
        if(!StringUtils.isEmpty(redirect)){
            return null;
        }
        String s = valueOps.get(key);
        if (StringUtils.isEmpty(s)) {
            return null;
        }
        return JSONObject.parseObject(s, SysUser.class);
    }

    /**
     * 用户登录状态保存到redis
     * 不会重新设置token
     *
     * @param appUser
     * @param request
     * @return
     */
    public static SysUser refreshCache(SysUser appUser, HttpServletRequest request) {
        String key = RedisKeys.getSystemUserSessionKey(appUser.getToken());
        valueOps.set(key, JSON.toJSONString(appUser), 1, TimeUnit.DAYS);
        HttpSession session = request.getSession();
        if (null == session) {
            logger.info("session 是空的");
            return null;
        }
        session.setAttribute(LoginUtil.LOGIN_USER_SESSION_KEY, appUser);
        return appUser;
    }

    /**
     * 用户登录状态保存到redis
     * 会重新设置token
     *
     * @param appUser
     * @param exchange
     * @return
     */
    /**
     * 用户登录状态保存到redis
     * 会重新设置token
     *
     * @param appUser
     * @param exchange
     * @return
     */
    public static SysUser saveInCache(SysUser appUser, HttpServletRequest request) {
        appUser.setToken(appUser.getToken());
        refreshCache(appUser, request);
        // 保存token和用户的关系, 是个 set
        String appUsernameByTokenSetKey = RedisKeys.getSystemUserSessionKey(appUser.getUserName());
        SetOperations<String, String> setOps = redisTemplate.opsForSet();
        setOps.add(appUsernameByTokenSetKey, appUser.getToken());
        // 清除set中的过期token, 避免set过大
        clearUserTokenSet(appUsernameByTokenSetKey);

        // 将用户对象存储到会话中
        HttpSession session = request.getSession();
        if (null == session) {
            logger.info("session 是空的");
            return null;
        }
        session.setAttribute(LOGIN_USER_SESSION_KEY, appUser);
        return appUser;
    }

    /**
     * 清除用户的登录token set避免过大
     *
     * @param appUsernameByTokenSetKey
     */
    private static void clearUserTokenSet(String appUsernameByTokenSetKey) {
        THREAD_POOL_EXECUTOR.submit(() -> {
            SetOperations<String, String> setOps = redisTemplate.opsForSet();
            Set<String> tokenSet = setOps.members(appUsernameByTokenSetKey);
            if (CollectionUtils.isEmpty(tokenSet)) {
                return;
            }
            tokenSet.forEach(token -> {
                String appUserSessionKey = RedisKeys.getSystemUserSessionKey(token);
                Long expire = redisTemplate.getExpire(appUserSessionKey);
                if (null == expire || expire < 0) {
                    logger.info("删除过期key {}", token);
                    setOps.remove(appUsernameByTokenSetKey, token);
                }
            });
        });
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        LoginUtil.redisTemplate = redisTemplate;
        LoginUtil.valueOps = redisTemplate.opsForValue();
    }
}
